package com.gmail.hh.flickrj.sample.android.data;

import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.RatingBar;

import com.gmail.hh.flickrj.sample.android.FlickrjMainActivity;
import com.gmail.hh.flickrj.sample.android.data.PhotolistData.IPhotolistZoomChanged;
import com.gmail.hh.flickrj.sample.android.helper.SettingsManager;
import com.googlecode.flickrjandroid.photos.Photo;


/**
 * a Wrapper for Photo and ImageView --- 
 * 
 * @author helmut
 * 
 */
public class PhotoData implements Comparable  { 
	
	private static final Logger logger = LoggerFactory.getLogger(PhotoData.class);
	
	// each Class which uses the zoomFaktor must implement this interface
	public interface IPhotoPositionShift {
		public void onPhotoPositionShift(String moveDirection);
	}

	
	public static final String IMAGE_CENTERROT_FALSE 		= "false";
	public static final String IMAGE_CENTERROT_CENTER 		= "C";
	public static final String IMAGE_CENTERROT_CENTERROT 	= "CR";
	
	private Photo photo;
	private ImageView imageViewSmallSquare ;
	private ImageView imageViewMedium ;
	private Drawable imageViewMediumDrawable ;
	private ImageSwitcher imageSwitcher ;
	
	// These matrices will be used to move and zoom image
	Matrix matrix = new Matrix();
	Matrix savedMatrix = new Matrix();
	// We can be in one of these 3 states
	static final int NONE = 0;
	static final int DRAG = 1;
	static final int ZOOM = 2;
	int mode = NONE;
	// Remember some things for zooming
	PointF startPoint0 = new PointF();
	PointF startPoint1 = new PointF();
	float startAngle;
	long startTime;
	PointF mid = new PointF();
	float oldDist = 1f;
	String savedItemClicked;
	

	// constructor 
	public PhotoData() {
		super();
	}



	public boolean applyTouchEvent(View v, MotionEvent event, IPhotoPositionShift callbackPhotoPositionShift) {
		
		    // Handle touch events here...
		    switch (event.getAction() & MotionEvent.ACTION_MASK) {
		    case MotionEvent.ACTION_DOWN:
		        savedMatrix.set(matrix);
		        startPoint0.set(event.getX(), event.getY());
		   //     logger.error("mode=DRAG");
		        mode = DRAG;
		        break;
		    case MotionEvent.ACTION_POINTER_DOWN:
	            startAngle = rotation(event);
		        oldDist = spacing(event);
		        startPoint0.set(event.getX(0), event.getY(0));
		        startPoint1.set(event.getX(1), event.getY(1));
		        if (oldDist > 10f) {
		            savedMatrix.set(matrix);
		            midPoint(mid, event);
		            mode = ZOOM;
		            startTime = new Date().getTime();
	//	            logger.error("mode=ZOOM");
		        }
		        break;
		    case MotionEvent.ACTION_UP:
                //check if there is a one-touch for shifting
		    	// when true, then reset the previous Matrix and throw another event for shifting
		    	// do shift, when the image is moved over the middle of the screen
		        if (mode == DRAG && callbackPhotoPositionShift!=null) {
			    	String moveDirection = null;
		        	ImageView imageView = null;
			        if (v instanceof ImageView) {
			        	imageView = (ImageView) v;
				    	int displayHeight  = imageView.getHeight();
				    	int displayHeightMiddle = displayHeight/2;
				    	int displayWidth  = imageView.getWidth();
				    	int displayWidthMiddle = displayWidth/2;
			 	    	int imageHeight  = imageView.getDrawable().getIntrinsicHeight();
			 	    	int imageWidth  = imageView.getDrawable().getIntrinsicWidth();
			 	    	float[] values = new float[9];
			 	    	matrix.getValues(values);
			            logger.error( "mode=DRAG and Actip_up - midDisp: " +displayWidthMiddle );
				        for  (int i=0; i<9; i++) {
				            logger.error( "  value: " +i + " :" + values[i] );
				        }
				        // in 0-4 ist scale-factor 
			            float scaleX = values[0];
			            float scaleY = values[4];
			            // in 2-5 ist die position x-y des punktes links-oben der drawable-bitmap
			            float translateX = values[2];
			            float translateY = values[5];
				        // abfragen und berechnungen funktionieren nur, wenn keine rotation gesetzt wurde; in 1-3 ist 0f!
				        if (values[1]==0f && values[3]==0f) {
				        	float scaleFactor = Math.max(scaleX, scaleY);
				        	  float	imageHeightFloat = imageHeight;
				        	  float	imageWidthFloat = imageWidth;
				        	  if (scaleFactor>0f) {
				        		  imageHeightFloat = imageHeightFloat * scaleFactor;
				        		  imageWidthFloat = imageWidthFloat * scaleFactor;
				        	  }
				        	  float leftImageBorder = translateX;
				        	  float rightImageBorder = translateX + imageWidthFloat;
				        	  float upperImageBorder = translateY;
				        	  float lowerImageBorder = translateY + imageHeightFloat;
				        	  if (leftImageBorder>displayWidthMiddle) {
				        		  moveDirection = PhotolistData.MOVEDIRECTION_BACKWARD;
				        	  }
				        	  if (rightImageBorder<displayWidthMiddle) {
				        		  moveDirection = PhotolistData.MOVEDIRECTION_FORWARD;
				        	  }
				        	  if (upperImageBorder>displayHeightMiddle) {
				        		  moveDirection = PhotolistData.MOVEDIRECTION_BACKWARD;
				        	  }
				        	  if (lowerImageBorder<displayHeightMiddle) {
				        		  moveDirection = PhotolistData.MOVEDIRECTION_FORWARD;
				        	  }
				        	  
				        }
			        }
			        if (moveDirection!=null && callbackPhotoPositionShift!=null) {
			    		callbackPhotoPositionShift.onPhotoPositionShift(moveDirection);
			        }
		     	}
	//	        logger.error( "mode=NONE");
		        mode = NONE;
		        break;
		    case MotionEvent.ACTION_POINTER_UP:
                //check if there is a double-touch for shifting
		    	// when true, then reset the previous Matrix and throw another event for shifting
		        if (mode == ZOOM && callbackPhotoPositionShift!=null) {
		            long endTime = new Date().getTime();
		            long spanTime = endTime - startTime;
			    	if (spanTime > 10 && spanTime < 3000 ) {  // check time between start/end
			    		String moveDirection = calculateDirection(startPoint0, startPoint1, event);
				    	if (moveDirection!=null) {  
				    		callbackPhotoPositionShift.onPhotoPositionShift(moveDirection);
				     	}
			     	}
		     	}
	//	        logger.error( "mode=NONE");
		        mode = NONE;
		        break;
		    case MotionEvent.ACTION_MOVE:
		        if (mode == DRAG) {
		            // ...
		            matrix.set(savedMatrix);
		            matrix.postTranslate(event.getX() - startPoint0.x, event.getY()
		                    - startPoint0.y);
		        } else if (mode == ZOOM) {
		            float newAngle = rotation(event);
		            float newDist = spacing(event);
		 //           logger.error( "newDist=" + newDist);
		            if (newDist > 10f) {
		                matrix.set(savedMatrix);
		                float scale = newDist / oldDist;
		                matrix.postScale(scale, scale, mid.x, mid.y);
		                float rotateAngleExact = newAngle-startAngle;
		                float rotationAcceleration = SettingsManager.getInstance().getRotationAcceleration();
		                int rotationSteps = SettingsManager.getInstance().getRotationSteps();  // only fixed angles for rotation
		                // convert into values between -180 and +180 because of acceleration	
			            if (rotateAngleExact > 180f ) rotateAngleExact-=360f;  
			            if (rotateAngleExact < -180f) rotateAngleExact+=360f;  
		                float rotateAngleResult = (rotateAngleExact * rotationAcceleration);
	        			rotateAngleResult = Math.round(rotateAngleResult/rotationSteps) *rotationSteps;
		                matrix.postRotate(rotateAngleResult, mid.x, mid.y);
		                
		            }
		        }
	        break;
		    }
		    if (imageViewSmallSquare!=null) {
				    imageViewSmallSquare.setImageMatrix(matrix);
		    }
		    if (imageViewMedium!=null) {
		    	imageViewMedium.setImageMatrix(matrix);
		    }
		return true;  	// stop propagation true/false
	    				// when return false, then its possible that move/zoom is failing
	}
	

	/** reset all actions */
	public void resetMatrix() {
    	matrix.reset();
	    if (imageViewSmallSquare!=null ) {
	    	imageViewSmallSquare.setImageMatrix(matrix);
	    }
	    if (imageViewMedium!=null ) {
	    	imageViewMedium.setImageMatrix(matrix);
	    }
	}
	
	/** center the photo , enlarge to the max.possible view-size*/
	public void centerMatrix(String rotationFit, ImageView imageView) {
		boolean center = false;
		boolean rotate = false;
		if (rotationFit!=null & rotationFit.equals( IMAGE_CENTERROT_CENTER ) ) {
	    	center = true;
	    }
		if (rotationFit!=null & rotationFit.equals( IMAGE_CENTERROT_CENTERROT ) ) {
	    	center = true;
			rotate = true;
	    }
		
        resetMatrix();
	    if (center) {
	    	//enlarge
	    	int imageWidth  = imageView.getDrawable().getIntrinsicWidth();
	    	int imageHeight = imageView.getDrawable().getIntrinsicHeight();
	    	int displayWidth  = imageView.getWidth();
	    	int displayHeight = imageView.getHeight();
	    	// rotate against clock-wise (-90); 
	    	// center is 0,0 because matrix in the xml-view places the image to left-top);
	    	// correct height-translation!
	    	boolean rotationForPortrait = false;
	    	boolean rotationForLandscape = false;
	    	int rotationAngle = 0;
	    	if (rotate && displayHeight>displayWidth && imageWidth>imageHeight ) {  //device:Portrait
	    		rotationAngle = -90; // against clock-wise
	    		rotationForPortrait = true;
		    }
	    	if (rotate && displayHeight<displayWidth && imageWidth<imageHeight ) {  //device:Landscape
	    		rotationAngle = +90; // clock-wise
	    		rotationForLandscape = true;
		    }
	    	if (rotationAngle!=0 ) {  // swap image-dimensions
	    		int origWidth = imageWidth;
	    		imageWidth = imageHeight;
	    		imageHeight = origWidth;     
	    	}
	    	float scaleX = (float)displayWidth  / (float)imageWidth;
	    	float scaleY = (float)displayHeight / (float)imageHeight;
	    	float minScale = Math.min(scaleX, scaleY);
	    	// center
	    	float marginX = (float) displayWidth  - imageWidth*minScale;
	    	float marginY = (float) displayHeight - imageHeight*minScale;
	    	// addition translation when rotated
	    	int corrHeightTranslation = 0;
	    	int corrWidthTranslation = 0;
	    	if (rotationAngle!=0 ) {
	    		matrix.postRotate(rotationAngle, 0, 0);
	    		if  (rotationForPortrait ) {  // Correct Height-Translation
		    		corrHeightTranslation = (int) (imageHeight*minScale);
		    	}
	    		if  (rotationForLandscape ) {  // Correct Width-Translation
	    			corrWidthTranslation = (int) (imageWidth*minScale);
		    	}
		    }

            matrix.postScale(minScale, minScale, 0f, 0f);
            matrix.postTranslate( marginX/2 + corrWidthTranslation , marginY/2 + corrHeightTranslation );

    	    if (imageView!=null) {
    	    	imageView.setImageMatrix(matrix);
    	    }
      //      logger.error("Image-Centering is done!" );
	    }
	}
	
	
	/** Determine the space between the first two fingers */
	private float spacing(MotionEvent event) {
	    float x = event.getX(0) - event.getX(1);
	    float y = event.getY(0) - event.getY(1);
	    return FloatMath.sqrt(x * x + y * y);
	}

	/** Calculate the mid point of the first two fingers */
	private void midPoint(PointF point, MotionEvent event) {
	    float x = event.getX(0) + event.getX(1);
	    float y = event.getY(0) + event.getY(1);
	    point.set(x / 2, y / 2);
	}

	/** calculate the angle of two points         */
    private float rotation(MotionEvent event) {
        double delta_x = (event.getX(0) - event.getX(1));
        double delta_y = (event.getY(0) - event.getY(1));
        double radians = Math.atan2(delta_y, delta_x);
     //   logger.error("Rotation: " + delta_x + " ## " + delta_y + " ## " + radians + " ## " + Math.toDegrees(radians));
        return (float) Math.toDegrees(radians);
    }

	/** check Direction of a double-touch        */
    private String calculateDirection(PointF startPoint0, PointF startPoint1, MotionEvent event) {  
    	String direction = null;
        double delta_0 = (event.getX(0) - startPoint0.x);
        double delta_1 = (event.getX(1) - startPoint1.x);
        double minDistance_x = 20.f; // movement of at least 30 pixel
    	if (delta_0 > minDistance_x && delta_1 > minDistance_x   ) {
    		direction = PhotolistData.MOVEDIRECTION_BACKWARD;
    	}
    	if (delta_0 < 0-minDistance_x && delta_1 < 0-minDistance_x   ) {
    		direction = PhotolistData.MOVEDIRECTION_FORWARD;
    	}
    	return direction;
    }
	
	// setter - getter
	public Photo getPhoto() {
		return photo;
	}
	public void setPhoto(Photo photo) {
		this.photo = photo;
	}

	public ImageView getImageViewSmallSquare() {
		return imageViewSmallSquare;
	}
	public void setImageViewSmallSquare(ImageView imageViewSmallSquare) {
		this.imageViewSmallSquare = imageViewSmallSquare;
	}
	public ImageView getImageViewMedium() {
		return imageViewMedium;
	}
	public void setImageViewMedium(ImageView imageViewMedium) {
		this.imageViewMedium = imageViewMedium;
	}
	public Drawable getImageViewMediumDrawable() {
		return imageViewMediumDrawable;
	}
	public void setImageViewMediumDrawable(Drawable imageViewMediumDrawable) {
		this.imageViewMediumDrawable = imageViewMediumDrawable;
	}
	public ImageSwitcher getImageSwitcher() {
		return imageSwitcher;
	}
	public void setImageSwitcher(ImageSwitcher imageSwitcher) {
		this.imageSwitcher = imageSwitcher;
	}



	// compare getSmallSquareUrl 
	@Override
	public int compareTo(Object arg0) {		
		PhotoData pd2 = (PhotoData) arg0;
		String id1 = this.getPhoto().getSmallSquareUrl();
		String id2 = pd2.getPhoto().getSmallSquareUrl();
		return id1.compareTo(id2);  
	}
	
	
	
	

}
